package net.ui;

import java.io.InputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.alibaba.fastjson.JSON;

import net.ui.model.action.ActionBean;
import net.ui.model.action.ActionGroupBean;
import net.ui.model.action.ActionSetBean;
import net.ui.model.action.ActionSetConfigBean;
import net.ui.model.constlist.ListData;
import net.ui.model.constlist.ListDataContainer;
import net.ui.model.constlist.ListDataFactory;
import net.ui.model.form.FormBean;
import net.ui.model.form.FormSetConfig;
import net.ui.model.panel.PanelBean;
import net.ui.model.panel.PanelConfig;
import net.ui.model.tab.TabbedConfig;
import net.ui.model.tab.TabbedPaneConfig;
import net.ui.model.table.TableBean;
import net.ui.model.table.TableConfig;
import net.ui.model.tree.TreeBean;
import net.ui.model.tree.TreeConfigAgent;

public abstract class UIConfigFactory {
	/**
	 * Logger for this class
	 */
	private static final Log logger = LogFactory.getLog(UIConfigFactory.class);

	private static final Map<String, Long> fileLastModifyMap = new HashMap<String, Long>();

	private boolean checkFileChange = false;

	private TabbedConfig tabConfig = null;

	private Map<String, TabbedPaneConfig> tabConfigMap = new HashMap<>();

	private TableConfig[] tableConfigs = null;

	private Map<String, TableBean> tableConfigMap = new HashMap<>();

	private PanelConfig[] panelConfigs = null;

	private Map<String, PanelBean> panelConfigMap = new HashMap<>();

	private FormSetConfig formSetConfig = null;

	private Map<String, FormBean> formConfigMap = new HashMap<>();

	private ActionSetBean actionSet = null;

	private Map<String, ActionBean> actionConfigMap = new HashMap<>();

	private Map<String, ActionGroupBean> actionGroupConfigMap = new HashMap<>();

	private ListDataFactory listDataFactory = null;

	private Map<String, ListDataContainer> listDataConfigMap = new HashMap<>();

	private String onlineDocPath;

	// private static UIConfigFactory instance;

	private static Map<String, UIConfigFactory> instanceMap = new HashMap<String, UIConfigFactory>();

	public UIConfigFactory() {
		instanceMap.put("", this);
	}

	public UIConfigFactory(String tag) {
		instanceMap.put(tag, this);
	}

	abstract protected String getUIFactoryTag();

	protected String getUICompentId(String compentId) {
		if (StringUtils.isBlank(getUIFactoryTag())) {
			return compentId;
		} else {
			return StringUtils.substringAfter(compentId, getUIFactoryTag() + ":");
		}
	}

	public static UIConfigFactory getInstance(String path) {
		if (path == null) {
			return instanceMap.get("");
		}
		
		String instancePath = path;
		
		if(StringUtils.startsWith(instancePath, "classpath:")) {
			instancePath = StringUtils.substringAfter(path, "classpath:");
		}
		
		String[] strs =  StringUtils.split(instancePath, ":");
		if (strs.length > 1) {
			return instanceMap.get(strs[0]);
		} else {
			return instanceMap.get("");
		}
	}

	public TabbedPaneConfig getTabConfig(String tabId) {

		try {

			if (isResourceChanged(getTabConfigFilePaths())) {
				initUITabConfig();
			}

		} catch (Exception e) {
			throw new RuntimeException(e);
		}

		TabbedPaneConfig tabbedPaneConfig = this.tabConfig.getTabbedPaneConfig(getUICompentId(tabId));

		if (tabbedPaneConfig == null) {
			logger.warn("can not found tab config by id [" + tabId + "]");
		}

		return tabbedPaneConfig;
	}

	public TableBean getTableConfig(String tableId) {

		try {

			if (isResourceChanged(getTableConfigFilePaths())) {
				initUITableConfig();
			}

		} catch (Exception e) {
			throw new RuntimeException(e);
		}

		TableBean tableBean = null;

		for (int i = 0; i < this.tableConfigs.length; i++) {
			TableConfig tableConfig = this.tableConfigs[i];
			tableBean = tableConfig.getTable(getUICompentId(tableId));
			if (tableBean != null) {
				break;
			}
		}

		if (tableBean == null) {
			logger.warn("can not found table config by id [" + tableId + "]");
		}

		return tableBean;
	}

	public PanelBean getPanelConfig(String panelId) {

		try {

			if (isResourceChanged(getPanelConfigFilePaths())) {
				initUIPanelConfig();
			}

		} catch (Exception e) {
			throw new RuntimeException(e);
		}

		PanelBean panelBean = null;

		for (int i = 0; i < this.panelConfigs.length; i++) {
			PanelConfig panelConfig = this.panelConfigs[i];
			panelBean = panelConfig.getPanel(getUICompentId(panelId));
			if (panelBean != null) {
				break;
			}
		}

		if (panelBean == null) {
			logger.warn("can not found panel config by id [" + panelId + "]");
		}

		return panelBean;
	}

	public FormBean getFormConfig(String formId) {

		try {

			if (isResourceChanged(getFormConfigFilePaths())) {
				initUIFormConfig();
			}

		} catch (Exception e) {
			throw new RuntimeException(e);
		}

		FormBean formBean = this.formSetConfig.getForm(getUICompentId(formId));

		if (formBean != null) {
			formBean.setFormModel(FormBean.EDIT_MODEL);
		}

		if (formBean == null) {
			logger.warn("can not found form config by id [" + formId + "]");
		}

		return formBean;
	}

	public ListDataContainer getListDataConfig(String aId) {

		try {

			if (isResourceChanged(getListConfigFilePaths())) {
				initUIListConfig();
			}

		} catch (Exception e) {
			throw new RuntimeException(e);
		}

		ListDataContainer listDataContainer = this.listDataFactory.getListDataContainer(getUICompentId(aId));

		if (listDataContainer == null) {
			logger.warn("can not found list config by id [" + aId + "]");
		}

		return listDataContainer;
	}

	public Map<Object, String> getListDataConfigMap(String aId) {

		ListDataContainer listDataContainer = this.listDataFactory.getListDataContainer(getUICompentId(aId));

		if (listDataContainer == null) {
			logger.warn("can not found list config by id [" + aId + "]");
		}

		Map<Object, String> map = new HashMap<Object, String>();

		for (Iterator<ListData> iterator = listDataContainer.getListData().iterator(); iterator.hasNext();) {
			ListData listData = iterator.next();
			map.put(listData.getData(), listData.getLabel());
		}

		return map;
	}

	public ListData[] getListDatas(String aId) {

		try {

			if (isResourceChanged(getListConfigFilePaths())) {
				initUIListConfig();
			}

		} catch (Exception e) {
			throw new RuntimeException(e);
		}

		return this.listDataFactory.getList(getUICompentId(aId));
	}

	public String getLocalMessage(String key, Locale locale) {
		return getLocalMessage(key, null, null, locale);
	}

	public String getLocalMessage(String key, String defaltMessage, Locale locale) {
		return getLocalMessage(key, null, defaltMessage, locale);
	}

	public String getLocalMessage(String key, Object[] args, Locale locale) {
		return getLocalMessage(key, args, null, locale);
	}

	public TreeBean getTreeConfig(String aId) {
		try {
			return TreeConfigAgent.getTreeConfig(getConfigResource(getUICompentId(aId)));
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

	public ActionGroupBean getActionGroupConfig(String actionGroupId) {

		try {

			if (isResourceChanged(getActionConfigFilePaths())) {
				initUIActionConfig();
			}

		} catch (Exception e) {
			throw new RuntimeException(e);
		}

		return actionSet.getActiongroupBean(getUICompentId(actionGroupId));
	}

	public ActionBean getActionBean(String actionBeanId) {
		try {

			if (isResourceChanged(getActionConfigFilePaths())) {
				initUIActionConfig();
			}

		} catch (Exception e) {
			throw new RuntimeException(e);
		}

		return actionSet.getActionBean(getUICompentId(actionBeanId));
	}

	public Map<String, Object> getUIConfigData() throws Exception {

		Map<String, Object> configMap = new HashMap<>();

		if (isResourceChanged(getTableConfigFilePaths()) || isResourceChanged(getActionConfigFilePaths())
				|| isResourceChanged(getFormConfigFilePaths()) || isResourceChanged(getListConfigFilePaths())
				|| isResourceChanged(getTabConfigFilePaths())) {
			initConfig();
		}

		configMap.put("actions", actionConfigMap);

		configMap.put("actionGroups", actionGroupConfigMap);

		configMap.put("listDatas", listDataConfigMap);

		configMap.put("forms", formConfigMap);

		configMap.put("tables", tableConfigMap);

		configMap.put("tabs", tabConfigMap);

		return configMap;

	}

	public static String getJSONConfigData() throws Exception {
		Set<String> tagKeys = instanceMap.keySet();

		Map<String, Object> configMap = new HashMap<>();

		for (Iterator<String> iterator = tagKeys.iterator(); iterator.hasNext();) {
			String tag = iterator.next();
			if (StringUtils.isEmpty(tag)) {
				configMap.putAll(instanceMap.get(tag).getUIConfigData());
			} else {
				configMap.put(tag, instanceMap.get(tag).getUIConfigData());
			}
		}

		return JSON.toJSONString(configMap);
	}

	public static Map<String, Object> getConfigMap() throws Exception {
		Set<String> tagKeys = instanceMap.keySet();

		Map<String, Object> configMap = new HashMap<>();

		for (Iterator<String> iterator = tagKeys.iterator(); iterator.hasNext();) {
			String tag = iterator.next();
			if (StringUtils.isEmpty(tag)) {
				configMap.putAll(instanceMap.get(tag).getUIConfigData());
			} else {
				configMap.put(tag, instanceMap.get(tag).getUIConfigData());
			}
		}

		return configMap;
	}

	public void initConfig() throws Exception {

		initUITabConfig();

		initUIListConfig();

		initUITableConfig();

		initUIFormConfig();

		initUIActionConfig();

		initUIPanelConfig();

	}

	public boolean isCheckFileChange() {
		return checkFileChange;
	}

	public void setCheckFileChange(boolean checkFileChange) {
		this.checkFileChange = checkFileChange;
	}

	protected boolean isResourceChanged(String[] paths) throws Exception {

		if (this.checkFileChange == false) {
			return false;
		}

		boolean changed = false;
		for (int i = 0; i < paths.length; i++) {
			String path = paths[i];
			if (fileLastModifyMap.containsKey(path) == false) {
				changed = true;
				break;
			} else if (fileLastModifyMap.get(path) != getResourceLastModfiy(path)) {
				changed = true;
				break;
			}
		}

		return changed;
	}

	protected void initUITabConfig() throws Exception {

		String[] files = getTabConfigFilePaths();

		tabConfig = new TabbedConfig();

		tabConfigMap.clear();

		for (int i = 0; i < files.length; i++) {

			tabConfig.getTabbedPaneList().addAll(TabbedConfig.parse(getConfigResource(files[i])).getTabbedPaneList());
			if (this.checkFileChange) {
				fileLastModifyMap.put(files[i], getResourceLastModfiy(files[i]));
			}
		}

		for (Iterator<TabbedPaneConfig> iterator = tabConfig.getTabbedPaneList().iterator(); iterator.hasNext();) {
			TabbedPaneConfig tabbedPaneConfig = iterator.next();
			tabConfigMap.put(tabbedPaneConfig.getId(), tabbedPaneConfig);

		}

		logger.info("Init Tab Config Success");
	}

	protected void initUIPanelConfig() throws Exception {

		String[] files = getPanelConfigFilePaths();

		if (ArrayUtils.isNotEmpty(files)) {

			panelConfigs = new PanelConfig[files.length];

			for (int i = 0; i < files.length; i++) {
				panelConfigs[i] = PanelConfig.init(getConfigResource(files[i]));
				if (this.checkFileChange) {
					fileLastModifyMap.put(files[i], getResourceLastModfiy(files[i]));
				}
			}
		}

		panelConfigMap.clear();

		if (panelConfigs != null) {
			for (int i = 0; i < panelConfigs.length; i++) {

				for (Iterator<PanelBean> iterator = panelConfigs[i].getPanelList().iterator(); iterator.hasNext();) {
					PanelBean panelBean = iterator.next();
					panelConfigMap.put(panelBean.getId(), panelBean);
				}
			}
		}

		logger.info("Init Panel Config Success");
	}

	protected void initUIListConfig() throws Exception {

		String[] files = getListConfigFilePaths();

		this.listDataFactory = new ListDataFactory();

		for (int i = 0; i < files.length; i++) {
			ListDataFactory tmpDataFactory = new ListDataFactory();
			tmpDataFactory.initListData(getConfigResource(files[i]));
			if (this.checkFileChange) {
				fileLastModifyMap.put(files[i], getResourceLastModfiy(files[i]));
			}
			this.listDataFactory.getListMap().putAll(tmpDataFactory.getListMap());
		}

		listDataConfigMap.clear();

		for (int i = 0; i < this.listDataFactory.getListDataContainers().length; i++) {

			listDataConfigMap.put(this.listDataFactory.getListDataContainers()[i].getName(),
					this.listDataFactory.getListDataContainers()[i]);
		}

		logger.info("Init List Config Success");
	}

	protected void initUITableConfig() throws Exception {

		String[] files = getTableConfigFilePaths();

		tableConfigs = new TableConfig[files.length];

		for (int i = 0; i < files.length; i++) {
			tableConfigs[i] = TableConfig.init(getConfigResource(files[i]));
			if (this.checkFileChange) {
				fileLastModifyMap.put(files[i], getResourceLastModfiy(files[i]));
			}
		}

		tableConfigMap.clear();

		for (int i = 0; i < tableConfigs.length; i++) {

			for (Iterator<TableBean> iterator = tableConfigs[i].getTableList().iterator(); iterator.hasNext();) {
				TableBean tableBean = iterator.next();
				tableConfigMap.put(tableBean.getId(), tableBean);
			}
		}

		logger.info("Init Table Config Success");
	}

	protected void initUIFormConfig() throws Exception {

		String[] files = getFormConfigFilePaths();

		formSetConfig = new FormSetConfig();

		for (int i = 0; i < files.length; i++) {
			formSetConfig.getFormList().addAll(FormSetConfig.init(getConfigResource(files[i])).getFormList());
			if (this.checkFileChange) {
				fileLastModifyMap.put(files[i], getResourceLastModfiy(files[i]));
			}
		}

		formConfigMap.clear();

		for (Iterator<FormBean> iterator = formSetConfig.getFormList().iterator(); iterator.hasNext();) {
			FormBean formBean = iterator.next();
			formConfigMap.put(formBean.getId(), formBean);
		}

		logger.info("Init Form Config Success");
	}

	protected void initUIActionConfig() throws Exception {

		String[] files = getActionConfigFilePaths();

		this.actionSet = new ActionSetBean();

		for (int i = 0; i < files.length; i++) {

			ActionSetConfigBean actionSetConfigBean = new ActionSetConfigBean();

			actionSetConfigBean.init(getConfigResource(files[i]), true);

			if (this.checkFileChange) {
				fileLastModifyMap.put(files[i], getResourceLastModfiy(files[i]));
			}
			ActionBean[] actionBeans = actionSetConfigBean.getActionSet().getActions();

			for (int j = 0; j < actionBeans.length; j++) {
				ActionBean actionBean = actionBeans[j];
				this.actionSet.addAction(actionBean);
			}

			ActionGroupBean[] actionGroupBeans = actionSetConfigBean.getActionSet().getActionGroups();

			for (int j = 0; j < actionGroupBeans.length; j++) {
				ActionGroupBean actionGroupBean = actionGroupBeans[j];
				this.actionSet.addActionGroup(actionGroupBean);
			}

		}

		ActionSetConfigBean.reBuildActions(actionSet, actionSet.getActions());

		for (int i = 0; i < actionSet.getActionGroups().length; i++) {
			ActionSetConfigBean.reBuildActions(actionSet, actionSet.getActionGroups()[i].getActionBeans());
		}

		actionConfigMap.clear();

		actionGroupConfigMap.clear();

		ActionBean[] actionBeans = actionSet.getActions();

		ActionGroupBean[] actionGroupBeans = actionSet.getActionGroups();

		for (int i = 0; i < actionBeans.length; i++) {
			actionConfigMap.put(actionBeans[i].getId(), actionBeans[i]);
		}

		for (int i = 0; i < actionGroupBeans.length; i++) {
			actionGroupConfigMap.put(actionGroupBeans[i].getId(), actionGroupBeans[i]);
		}

		logger.info("Init Action Config Success");
	}

	public String getOnlineDocPath() {
		return onlineDocPath;
	}

	public void setOnlineDocPath(String onlineDocPath) {
		this.onlineDocPath = onlineDocPath;
	}

	public Map<String, TabbedPaneConfig> getTabConfigMap() {
		return tabConfigMap;
	}

	public Map<String, TableBean> getTableConfigMap() {
		return tableConfigMap;
	}

	public PanelConfig[] getPanelConfigs() {
		return panelConfigs;
	}

	public Map<String, PanelBean> getPanelConfigMap() {
		return panelConfigMap;
	}

	public Map<String, FormBean> getFormConfigMap() {
		return formConfigMap;
	}

	public Map<String, ActionBean> getActionConfigMap() {
		return actionConfigMap;
	}

	public Map<String, ActionGroupBean> getActionGroupConfigMap() {
		return actionGroupConfigMap;
	}

	public Map<String, ListDataContainer> getListDataConfigMap() {
		return listDataConfigMap;
	}

	abstract public String[] getTabConfigFilePaths();

	abstract public String[] getTableConfigFilePaths();

	abstract public String[] getPanelConfigFilePaths();

	abstract public String[] getFormConfigFilePaths();

	abstract public String[] getListConfigFilePaths();

	abstract public String[] getActionConfigFilePaths();

	abstract public String[] getIconLibFilePaths();

	abstract protected long getResourceLastModfiy(String resourcePath) throws Exception;

	abstract protected InputStream getConfigResource(String resourcePath) throws Exception;

	abstract public String getLocalMessage(String key, Object[] args, String defaltMessage, Locale locale);

}
